home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UGroupDoc.cp < prev    next >
Encoding:
Text File  |  1994-03-13  |  13.0 KB  |  550 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UGroupDoc.cp
  3.  
  4. #include "UGroupDoc.h"
  5. #include "UPrefsDatabase.h"
  6. #include "UDiscListView.h"
  7. #include "UHeaderList.h"
  8. #include "UDiscList.h"
  9. #include "UGroupDocCmds.h"
  10. #include "UNewsAppl.h"
  11. #include "StreamTools.h"
  12. #include "UThread.h"
  13. #include "UBufferedFileStream.h"
  14.  
  15. #include <ErrorGlobals.h>
  16. #include <RsrcGlobals.h>
  17.  
  18. #include <UPrinting.h>
  19.  
  20. #include <ToolUtils.h>
  21.  
  22. #pragma segment MyGroup
  23.  
  24. #define qDebugLock qDebug
  25. #define qDebugSave qDebug & 0
  26.  
  27.  
  28. const long kCurrentDatabaseVersion = 19;
  29. const long kMinDatabaseVersion = 19;
  30.  
  31. const long kCurrentArticleStatusVersion = 2;
  32. const long kMinArticleStatusVersion = 1;
  33.  
  34.  
  35. TGroupDoc::TGroupDoc()
  36. {
  37. }
  38.  
  39. pascal void TGroupDoc::Initialize()
  40. {
  41.     inherited::Initialize();
  42.     fSavePrintInfo = false; // couldn't get it to create a print record...
  43.     fDiscList = nil;
  44.     fDiscListWindow = nil;
  45.     fDiscListView = nil;
  46.     fGroupDotName = "(program error)";
  47.     fOldArticleStatus = nil;
  48.     fArticleStatus = nil;
  49.     fSubjectHdr = nil;
  50.     fFromHdr = nil;
  51.     fMsgIDHdr = nil;
  52.     fRefsHdr = nil;
  53.     fSaveArticleStatus = true;
  54.     fSaveDatabase = true;
  55.     fLockLevelAgainstClose = 0;
  56.     fFirstArticleID = 0;
  57.     fLastArticleID = 0;
  58.     fReopenAlert = false;
  59.     fDBFileHandler = nil;
  60.     fArticleStatusIsDirty = false;
  61.     fDatabaseIsDirty = false;
  62.     fIdentifier = kArticleStatusFileType;
  63. }
  64.  
  65. void TGroupDoc::IGroupDoc(TFile *statFile, TFile *dbFile, const CStr255 &dotName)
  66. {
  67.     inherited::IFileBasedDocument(statFile, kScrapType);
  68.     FailInfo fi;
  69.     if (fi.Try())
  70.     {
  71.         fGroupDotName = dotName;
  72.         fTitle = dotName;
  73.  
  74.         fDBFileHandler = this->DoMakeFileHandler(dbFile);
  75.         
  76.         TArticleStatus *as;
  77.         as = new TArticleStatus();
  78.         as->IArticleStatus(this);
  79.         fOldArticleStatus = as;
  80.         as = new TArticleStatus();
  81.         as->IArticleStatus(this);
  82.         fArticleStatus = as;
  83.     
  84.         PDiscList *dl = new PDiscList();
  85.         dl->IDiscList();
  86.         fDiscList = dl;
  87.  
  88.         fSubjectHdr = NewHeaderList();
  89.         fFromHdr = NewHeaderList();
  90.         fMsgIDHdr = NewHeaderList();
  91.         fRefsHdr = NewHeaderList();
  92.  
  93.         fi.Success();
  94.     }
  95.     else // fail
  96.     {
  97.         Free();
  98.         fi.ReSignal();
  99.     }
  100. }
  101.  
  102. pascal void TGroupDoc::Free()
  103. {
  104.     FreeIfObject(fOldArticleStatus);    fOldArticleStatus = nil;
  105.     FreeIfObject(fArticleStatus);            fArticleStatus = nil;
  106.  
  107.     delete fDiscList;                                    fDiscList = nil;
  108.     
  109.     FreeIfObject(fSubjectHdr);                fSubjectHdr = nil;
  110.     FreeIfObject(fFromHdr);                        fFromHdr = nil;
  111.     FreeIfObject(fMsgIDHdr);                    fMsgIDHdr = nil;
  112.     FreeIfObject(fRefsHdr);                        fRefsHdr = nil;
  113.  
  114.     FreeIfObject(fDBFileHandler);            fDBFileHandler = nil;
  115.     fDiscListWindow = nil; // view
  116.     fDiscListView = nil; // view
  117.     inherited::Free();
  118. }
  119.  
  120. pascal void TGroupDoc::Close()
  121. {
  122.     if (fLockLevelAgainstClose > 0)
  123.     {
  124.         gApplication->Beep(0);
  125. #if qDebug
  126.         fprintf(stderr, "TGroupDoc::Close called when fLockLevelAgainstClose is %ld\n", fLockLevelAgainstClose);
  127. #endif
  128.         Failure(0, 0); // cannot close now, should probably not be silent
  129.     }
  130.     if (fDiscListView)
  131.         fDiscListView->CloseDown();
  132.     {
  133.         CWindowIterator iter(this, 2, kMaxLong, false);
  134.         for (TWindow *aWindow = iter.FirstWindow(); iter.More(); aWindow = iter.NextWindow())
  135.         {    
  136.             if (!aWindow->fFloats && aWindow != fDiscListWindow)
  137.                 aWindow->CloseAndFree();
  138.         }
  139.     }
  140.     SetChangeCount(0); // so we can close if save fails
  141.     SaveDocument(cClose);
  142.     inherited::Close();
  143. }
  144.  
  145. pascal void TGroupDoc::DoRead(TFile *aFile, Boolean /* forPrinting */)
  146. {
  147.     TStream *aStream = NewBufferedFileStream(aFile, 16 * 1024, 0);
  148.     VOLATILE(aStream);
  149.     FailInfo fi;
  150.     if (fi.Try())
  151.     {
  152.         if (aStream->GetSize() >= 4)
  153.         {
  154.             OSType fileType;
  155.             FailOSErr(aFile->GetFileType(fileType));
  156.             switch (fileType)
  157.             {
  158.                 case kArticleStatusFileType:
  159.                     DoReadArticleStatus(aStream);
  160.                     break;
  161.                     
  162.                 case kGroupDBFileType:
  163.                     DoReadDatabase(aStream);
  164.                     break;
  165.                 
  166.                 default:
  167.                     Failure(minErr, 0);
  168.             }
  169.         }
  170.         aStream->Free(); aStream = nil;
  171.         fi.Success();
  172.     }
  173.     else // fail
  174.     {
  175.         FreeIfObject(aStream); aStream = nil;
  176.         fi.ReSignal();
  177.     }
  178. }
  179.  
  180. pascal void TGroupDoc::DoWrite(TFile *aFile, Boolean /* forPrinting */)
  181. {
  182.     FailOSErr(aFile->SetDataMark(0, fsFromStart)); 
  183.     TStream *aStream = NewBufferedFileStream(aFile, 0, 16 * 1024);
  184.     VOLATILE(aStream);
  185.     FailInfo fi;
  186.     if (fi.Try())
  187.     {
  188.         OSType fileType;
  189.         FailOSErr(aFile->GetFileType(fileType));
  190.         switch (fileType)
  191.         {
  192.             case kArticleStatusFileType:
  193.                 DoWriteArticleStatus(aStream);
  194.                 break;
  195.                 
  196.             case kGroupDBFileType:
  197.                 DoWriteDatabase(aStream);
  198.                 break;
  199.             
  200.             default:
  201.                 Failure(minErr, 0);
  202.         }
  203.         aStream->Free(); aStream = nil;
  204.         fi.Success();
  205.     }
  206.     else // fail
  207.     {
  208.         FreeIfObject(aStream); aStream = nil;
  209.         fi.ReSignal();
  210.     }
  211. }
  212.  
  213. pascal void TGroupDoc::DoNeedDiskSpace(TFile *itsFile, long &dataForkBytes,  long &rsrcForkBytes)
  214. {
  215.     if (itsFile == fDBFileHandler->GetFile())
  216.         DatabaseDoNeedDiskSpace(itsFile, dataForkBytes, rsrcForkBytes);
  217.     else
  218.         ArticleStatusDoNeedDiskSpace(itsFile, dataForkBytes, rsrcForkBytes);
  219. }
  220.  
  221. //........................................................................
  222. void TGroupDoc::DoReadDatabase(TStream *aStream)
  223. {
  224.     long version = aStream->ReadLong();
  225.     if (!MyCheckVersion(version, kMinDatabaseVersion, kCurrentDatabaseVersion, "TGroupDoc"))
  226.         return;
  227.     gCurThread->CheckYield();
  228.     fDiscList->DoRead(aStream);
  229.     gCurThread->CheckYield();
  230.     fSubjectHdr->DoRead(aStream);
  231.     gCurThread->CheckYield();
  232.     fFromHdr->DoRead(aStream);    
  233.     gCurThread->CheckYield();
  234.     fFirstArticleID = aStream->ReadLong();
  235.     fLastArticleID = aStream->ReadLong();
  236. }
  237.  
  238. void TGroupDoc::DoWriteDatabase(TStream *aStream)
  239. {
  240.     gCurThread->CheckYield();
  241.     aStream->WriteLong(kCurrentDatabaseVersion);
  242.     fDiscList->DoWrite(aStream);
  243.     gCurThread->CheckYield();
  244.     fSubjectHdr->DoWrite(aStream);
  245.     gCurThread->CheckYield();
  246.     fFromHdr->DoWrite(aStream);
  247.     gCurThread->CheckYield();
  248.     aStream->WriteLong(fFirstArticleID);
  249.     aStream->WriteLong(fLastArticleID);
  250. }
  251.  
  252. void TGroupDoc::DatabaseDoNeedDiskSpace(TFile *itsFile, long &dataForkBytes,  long &rsrcForkBytes)
  253. {
  254.     dataForkBytes += sizeof(long); // version
  255.     dataForkBytes += fDiscList->NeededDiskSpace();
  256.     fSubjectHdr->DoNeedDiskSpace(dataForkBytes);
  257.     fFromHdr->DoNeedDiskSpace(dataForkBytes);
  258.     dataForkBytes += 2 * sizeof(long); // fFirstArticleID, fLastArticleID
  259.     inherited::DoNeedDiskSpace(itsFile, dataForkBytes, rsrcForkBytes);
  260. }
  261.  
  262. //........................................................................
  263.  
  264. void TGroupDoc::DoReadArticleStatus(TStream *aStream)
  265. {
  266.     long version = aStream->ReadLong();
  267.     MyStreamCheckVersion(version, kMinArticleStatusVersion, kCurrentArticleStatusVersion, "TGroupDoc/ArticleStatus");
  268.     if (version == 1)
  269.     {
  270.         aStream->ReadLong(); // skip old IP# check
  271.         fOldArticleStatus->DoIronAgeFormatRead(aStream);
  272.     }
  273.     else
  274.     {
  275.         fOldArticleStatus->DoRead(aStream);
  276.     }
  277.     fArticleStatus->SpecifyWithArticleStatus(fOldArticleStatus);
  278. }
  279.  
  280. void TGroupDoc::DoWriteArticleStatus(TStream *aStream)
  281. {
  282.     aStream->WriteLong(kCurrentArticleStatusVersion);
  283.     fArticleStatus->DoWrite(aStream);
  284. }
  285.  
  286. void TGroupDoc::ArticleStatusDoNeedDiskSpace(TFile * /* itsFile */, long &dataForkBytes,  long & /* rsrcForkBytes */)
  287. {
  288.     dataForkBytes += sizeof(long); // version
  289.     fArticleStatus->DoNeedDiskSpace(dataForkBytes);
  290. }
  291.  
  292. //........................................................................
  293.  
  294. void TGroupDoc::DoPostUpdate(Boolean updateOnly)
  295. {
  296.     if (!updateOnly)
  297.     {
  298.     }
  299. }
  300.  
  301. pascal TFile *TGroupDoc::DoMakeFile(CommandNumber itsCommandNumber)
  302. {
  303.     switch (itsCommandNumber)
  304.     {
  305.         case cSaveArticleStatus:
  306.             return NewFile(kArticleStatusFileType, kSignature, kUsesDataFork, noResourceFork, kDataOpen, !kRsrcOpen);
  307.  
  308.         case cSaveGroupDB:
  309.             return NewFile(kGroupDBFileType, kSignature, kUsesDataFork, noResourceFork, !kDataOpen, !kRsrcOpen);
  310.         
  311.         default:
  312.             FailOSErr(errNotImplemented);
  313.     }
  314. }
  315.  
  316. pascal void TGroupDoc::ReadDocument(Boolean forPrinting)
  317. {
  318.     fFileHandler->ReadFile(forPrinting);
  319.     fDBFileHandler->ReadFile(forPrinting);
  320. }
  321.  
  322. pascal void TGroupDoc::SaveDocument(CommandNumber /* itsCommandNumber */)
  323. {
  324.     CommitLastCommand();
  325.     // if it has not been saved, SaveDocument asks for a filename
  326.     // so just save it without checks first time!
  327. #if qDebugSave
  328.     fprintf(stderr, "TGroupDoc::SaveDoc, fileExists: AS: %hd, DB: %hd\n", fFileHandler->FileExists(), fDBFileHandler->FileExists());
  329. #endif
  330.  
  331.     if (fFirstArticleID > fLastArticleID)
  332.     {
  333.         fArticleStatusIsDirty = false; // don't save them...
  334.         fDatabaseIsDirty = false;
  335.         if (fFileHandler->FileExists())
  336.         {
  337. #if qDebug
  338.             fprintf(stderr, "TGroupDoc::SaveDocument, no articles, deletes AS\n");
  339. #endif
  340.             fFileHandler->GetFile()->CloseFile();
  341.             fFileHandler->GetFile()->DeleteFile();
  342.             fFileHandler->fFileExists = false;
  343.         }
  344.         if (fDBFileHandler->FileExists())
  345.         {
  346. #if qDebug
  347.             fprintf(stderr, "TGroupDoc::SaveDocument, no articles, deletes DB\n");
  348. #endif
  349.             fDBFileHandler->GetFile()->CloseFile();
  350.             fDBFileHandler->GetFile()->DeleteFile();
  351.             fDBFileHandler->fFileExists = false;
  352.         }
  353.     }
  354.  
  355.     if (fSaveArticleStatus && fArticleStatusIsDirty)
  356.     {
  357. #if qDebugSave
  358.         fprintf(stderr, "Saves article status as it was dirty\n");
  359. #endif
  360.         fArticleStatusIsDirty = false;
  361.         if (fFileHandler->FileExists())
  362.         {
  363.             CheckFile(kIDBuzzString, bzSaveAnyways, FALSE);
  364.             fFileHandler->SaveFile(cSaveArticleStatus, false, true, false);
  365.         }
  366.         else
  367.             fFileHandler->SaveFile(cSaveArticleStatus, false, false, false);
  368.     }
  369.     
  370.     if (fSaveDatabase && fDatabaseIsDirty)
  371.     {
  372. #if qDebugSave
  373.         fprintf(stderr, "Saves group database as it was dirty\n");
  374. #endif
  375.         fDatabaseIsDirty = false;
  376.         if (fDBFileHandler->FileExists())
  377.         {
  378.             CheckDBFile(kIDBuzzString, bzSaveAnyways, FALSE);
  379.             fDBFileHandler->SaveFile(cSaveGroupDB, false, true, false);
  380.         }
  381.         else
  382.             fDBFileHandler->SaveFile(cSaveGroupDB, false, false, false);
  383.     }
  384.     SetChangeCount(0);
  385. }    
  386.  
  387. pascal void TGroupDoc::Changed(ChangeID theChange, TObject* changedBy)
  388. {
  389.     switch (theChange)
  390.     {
  391.         case cArticleStatusChange:
  392.             fArticleStatusIsDirty = true;
  393.             break;
  394.         
  395.         case cDatabaseUpdated:
  396.             fDatabaseIsDirty = true;
  397.             break;
  398.         
  399.         default:
  400.             break;
  401.     }
  402.     inherited::Changed(theChange, changedBy);
  403. }
  404.  
  405. void TGroupDoc::CheckDBFile(ResNumber rsrcId, short rsrcIndex, Boolean reverting)
  406. {
  407.     OSErr err = fDBFileHandler->FileChanged(reverting);    // don't care about the file type if saving 
  408.     if (err == errFileChanged)
  409.     {
  410.         CStr255 name = fTitle;
  411.         CStr255 s;
  412.         GetIndString(s, rsrcId, rsrcIndex);
  413.         ParamText(name, s, gEmptyString, gEmptyString);
  414.         if (MacAppAlert(phFileChanged, NULL) == cancel)// !!! This should be programatically defeatable 
  415.             Failure(noErr, messageCancelled);
  416.     }
  417.     else if ((err != noErr) && reverting)
  418.         Failure(err, 0);
  419. }
  420.  
  421. pascal void TGroupDoc::DoInitialState()
  422. {
  423.     Failure(errNotImplemented, messageNewFailed);
  424. }
  425.  
  426. pascal void TGroupDoc::DoMakeViews(Boolean forPrinting)
  427. {
  428.     if (forPrinting)
  429.         return;
  430.     fDiscListWindow = gViewServer->NewTemplateWindow(kDiscListView, this);
  431.     if (!fDiscListWindow)
  432.     {
  433. #if qDebug
  434.         ProgramBreak("Could not create window");
  435. #endif
  436.         FailNIL(fDiscListWindow);
  437.     }
  438.     fDiscListView = (TDiscListView*) fDiscListWindow->FindSubView(kDiscListViewID);
  439. #if qDebug
  440.     if (!IsObject(fDiscListView))
  441.         DebugStr(CStr255("UPS: fDiscListView IS NOT OBJECT"));
  442. #endif
  443.     FailNIL(fDiscListView);
  444.  
  445.     TStdPrintHandler *aHandler = new TStdPrintHandler;
  446.     aHandler->IStdPrintHandler(this, fDiscListView, !kSquareDots, kFixedSize, !kFixedSize);
  447.  
  448.     CRect maxResize(fDiscListWindow->fResizeLimits);
  449.     maxResize.right = 650; //@@ hack
  450.     fDiscListWindow->SetResizeLimits(maxResize[topLeft], maxResize[botRight]);
  451.     Lock(true);
  452.     fDiscListWindow->SetTitle(fGroupDotName);
  453.     Lock(false);
  454.     gPrefs->GetSilentWindowPosPrefs('WDis', fDiscListWindow);
  455.     fDiscListView->Startup(fGroupDotName);
  456. }
  457.  
  458. pascal void TGroupDoc::FileHasBeenSaved(const CStr255& /* newName */)
  459. {
  460.     // No way. May not change the title when saving
  461. }
  462.  
  463. TArticleStatus *TGroupDoc::GetArticleStatus()
  464. {
  465.     return fArticleStatus;
  466. }
  467.  
  468. TArticleStatus *TGroupDoc::GetOldArticleStatus()
  469. {
  470.     return fOldArticleStatus;
  471. }
  472.  
  473. PDiscList *TGroupDoc::GetDiscList()
  474. {
  475.     return fDiscList;
  476. }
  477.  
  478. TDiscListView *TGroupDoc::GetDiscListView()
  479. {
  480.     return fDiscListView;
  481. }
  482.  
  483. void TGroupDoc::GetGroupDotName(CStr255 &name)
  484. {
  485.     name = fGroupDotName;
  486. }
  487.  
  488. Boolean TGroupDoc::GetFrom(long id, HandleOffsetLength &hol)
  489. {
  490.     if (id < fFirstArticleID || id > fLastArticleID)
  491.         return false;
  492.     fFromHdr->AtGet(id, hol);
  493.     return true;
  494. }
  495.  
  496. Boolean TGroupDoc::GetSubject(long id, HandleOffsetLength &hol)
  497. {
  498.     if (id < fFirstArticleID || id > fLastArticleID)
  499.         return false;
  500.     fSubjectHdr->AtGet(id, hol);
  501.     return true;
  502. }
  503.  
  504. long TGroupDoc::GetFirstArticleID()
  505. {
  506.     return fFirstArticleID;
  507. }
  508.  
  509. long TGroupDoc::GetLastArticleID()
  510. {
  511.     return fLastArticleID;
  512. }
  513.  
  514. void TGroupDoc::IncLockAgainstClose()
  515. {
  516. #if qDebugLock
  517.     fprintf(stderr, "TGroupDoc::IncLockAgainstClose, level %ld -> %ld\n", fLockLevelAgainstClose, fLockLevelAgainstClose + 1);
  518. #endif
  519.     fLockLevelAgainstClose++;
  520. }
  521.  
  522. void TGroupDoc::DecLockAgainstClose()
  523. {
  524. #if qDebugLock
  525.     fprintf(stderr, "TGroupDoc::DecLockAgainstClose, level %ld -> %ld\n", fLockLevelAgainstClose, fLockLevelAgainstClose - 1);
  526. #endif
  527. #if qDebug
  528.     if (fLockLevelAgainstClose == 0)
  529.     {
  530.         ProgramBreak("wanted to unlock and fLockLevelAgainstClose is zero");
  531.         return;
  532.     }
  533. #endif
  534.     fLockLevelAgainstClose--;
  535. }
  536.  
  537. pascal void TGroupDoc::DoSetupMenus()
  538. {
  539.     inherited::DoSetupMenus();
  540. }
  541.  
  542. pascal void TGroupDoc::DoMenuCommand(CommandNumber aCommandNumber)
  543. {
  544.     switch (aCommandNumber)
  545.     {
  546.         default: 
  547.             inherited::DoMenuCommand(aCommandNumber);
  548.     }
  549. }
  550.